E) Tag Dispatching

템플릿 부분 특수화(template partial specialization; SFINAE)을 이용하면, 기존 템플릿에서
템플릿 인자에 따라 다른 코드를 구현할 수 있다.
template <typename T1, typename T2>
auto mul(T1 v1, T2 v2){
return v1*v2;
}
template <typename T1, typename T2, std::enable_if_t<std::is_floating_point<T1>::value &&
!std::is_same<T1, long double>::value, int>=0>
auto mul(T1 v1, T2 v2){
std::cout<<v1<<"is floating point!"<<std::endl;
return v1*v2;
}
template <typename T1, typename T2, std::enable_if_t<std::is_integral<T1>::value &&
!std::is_same<T1, long int>::value, int>=0>
auto mul(T1 v1, T2 v2){
std::cout<<v1<<"is integral!"<<std::endl;
return v1*v2;
}
template <typename T1, typename T2, std::enable_if_t<std::is_same<T1, long int>::value ||
std::is_same<T1, long double>::value, int>=0>
auto mul(T1 v1, T2 v2){
std::cout<<v1<<"is long?"<<std::endl;
return v1*v2;
}
위와 같이 SFINAE는 유용하지만, 경우가 추가되면 기능이 바뀌지 않는 기존의 함수를 수정해야 하고,
코드가 지저분해준다.
꼬리표 분배(tag dispatching)
타입에 따라 꼬리표를 분배하는 기능 추가, 함수의 호출 깊이가 1칸 증가함
struct integral_type{};
struct float_type{};
struct long_type{};
template <typename T>
using what_type=std::conditional_t<std::is_same<T, long>::value || std::is_same<T, long double>::value, long_type, std::conditional_t<std::is_integral<T>::value, integral_type, float_type>>;
template <typename T1, typename T2>
auto mul_impl(T1 v1, T2 v2, long_type){
std::cout<<v1<<"is long type!"<<std::endl;
return v1*v2;
}
template <typename T1, typename T2>
auto mul_impl(T1 v1, T2 v2, integral_type){
std::cout<<v1<<"is integral type!"<<std::endl;
return v1*v2;
}
template <typename T1, typename T2>
auto mul_impl(T1 v1, T2 v2, float_type){
std::cout<<v1<<"is floating point tpye!"<<std::endl;
return v1*v2;
}
template <typename T1, typename T2>
auto mul(T1 v1, T2 v2){
return mul_impl(v1, v2, what_type<T1>());
}
mul 함수가 호출되면, what_type를 통해서 미리 정의된 꼬리표를 받게 되고,
이를 객체화 시켜 오버로드된 mul_impl 함수로 전달한다.
(mul_impl 함수의 꼬리표 인자는 객체화 시켜서 전달됨)

이후 새로운 타입이 추가된다면, what_type의 명세만 수정하고 새로운 mul_impl을 오버로드해서 구현가능하다.